﻿using Dapper;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace Asion.Dapper
{
    /// <summary>
    /// 缓存相关信息
    /// </summary>
    public static partial class SqlMapperExtensions
    {
        internal static ConcurrentDictionary<PropertyIdentity, CustomTableMapping> _properties = new();

        internal enum PropertyListType
        {
            All = 0,
            Key,//id or kye
            Select,//select

        }

        /// <summary>
        /// 属性身份
        /// </summary>
        public class PropertyIdentity : IEquatable<PropertyIdentity>
        {
            //internal PropertyListType PropertyListType { get; set; }

            internal Type ClassType { get; set; }


            /// <summary>
            /// The hash code of this Identity.
            /// </summary>
            public readonly int hashCode;

            /// <summary>
            /// Gets the hash code for this identity.
            /// </summary>
            /// <returns></returns>
#pragma warning disable CS0618 // Type or member is obsolete
            public override int GetHashCode() => hashCode;
#pragma warning restore CS0618 // Type or member is obsolete

            internal string TypeName
            {
                get
                {
                    return ClassType.AssemblyQualifiedName;
                }
            }


            internal PropertyIdentity(Type type)
            {

#pragma warning disable CS0618 // Type or member is obsolete
                //this.PropertyListType = propertyListType;
                this.ClassType = type;
                unchecked
                {
                    hashCode = 17; // we *know* we are using this in a dictionary, so pre-compute this
                    hashCode = (hashCode * 23) + ClassType.GetHashCode();
                    //hashCode = (hashCode * 23) + PropertyListType.GetHashCode();
                    hashCode = (hashCode * 23) + TypeName.GetHashCode();
                }
#pragma warning restore CS0618 // Type or member is obsolete


            }

            public bool Equals(PropertyIdentity? other)
            {
                if (ReferenceEquals(this, other)) return true;
                if (other is null) return false;
#pragma warning disable CS0618 // Type or member is obsolete
                return TypeName == other.TypeName
                    && ClassType == other.ClassType;
#pragma warning restore CS0618 // Type or member is obsolete
                throw new NotImplementedException();
            }
        }

        internal static CustomTableMapping GetPropertiesCache(PropertyIdentity propertyIdentity)
        {
            _properties.TryGetValue(propertyIdentity, out CustomTableMapping result);
            return result;
        }






        /// <summary>
        ///  Properties init
        /// </summary>
        public static void PropertiesInitialize()
        {
            //  select out all impl IEntity model
            //扫描core中的类型

            var types = AssemblyHelper.GetTypeByDirectory(typeof(IEntity), DapperConfiguration.default_scan_name);
            foreach (var type in types)
            {
                var tableName = _table_name_resolver.ResolveTableName(type);
                var table = new CustomTableMapping()
                {
                    Type = type,
                    TableName = tableName,
                    Columns = new List<CustomColumnMapping>()
                };

                foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    var customInfo = new CustomColumnMapping().Initialize(property);
                    if (customInfo.IsKey)
                    {
                        table.KeyColumn = customInfo.DataColumnName;
                    }
                    table.Columns.Add(customInfo);

                }
                table.ColumnsNameRelation = table.Columns.ToDictionary(a => a.ColumnName);
                table.DataColumnsNameRelation = table.Columns.ToDictionary(a => a.DataColumnName);
                _properties.TryAdd(new PropertyIdentity(type), table);

            }
        }



        /// <summary>
        /// custom table mapping
        /// </summary>
        public class CustomTableMapping
        {
            /// <summary>
            /// table name
            /// </summary>
            public string TableName { get; set; }


            /// <summary>
            /// column
            /// </summary>
            public List<CustomColumnMapping> Columns { get; set; }


            /// <summary>
            /// 主键
            /// </summary>
            public string KeyColumn { get; set; }

            /// <summary>
            /// Class Type
            /// </summary>
            public Type Type { get; set; }

            /// <summary>
            /// column name relation
            /// </summary>
            public IDictionary<string, CustomColumnMapping> ColumnsNameRelation { get; set; }


            /// <summary>
            /// data column name relation
            /// </summary>
            public IDictionary<string, CustomColumnMapping> DataColumnsNameRelation { get; set; }

            /// <summary>
            /// check is caontain column
            /// </summary>
            /// <returns></returns>
            public CustomColumnMapping GetColumnMapping(string column)
            {
                if (ColumnsNameRelation.ContainsKey(column))
                {
                    return ColumnsNameRelation[column];
                }

                if (DataColumnsNameRelation.ContainsKey(column))
                {
                    return DataColumnsNameRelation[column];
                }
                return null;

            }



            /// <summary>
            /// check is caontain column
            /// </summary>
            /// <returns></returns>
            public List<CustomColumnMapping> GetSelectColumnMapping()
            {
                var result = Columns.Where(a => a.IsSupportSelect).ToList();
                return result;

            }
        }


        /// <summary>
        /// custom  column mapping
        /// </summary>
        public class CustomColumnMapping
        {
            /// <summary>
            /// column name
            /// </summary>
            public string ColumnName { get; set; }

            /// <summary>
            /// data column name
            /// </summary>
            public string DataColumnName { get; set; }
            /// <summary>
            /// is key
            /// <para>KeyAttribute</para>
            /// </summary>
            public bool IsKey { get; set; }

            /// <summary>
            /// is code
            /// </summary>
            public bool IsCode { get; set; }

            /// <summary>
            /// coluns type
            /// </summary>
            public Type ColumnType { get; set; }


            /// <summary>
            /// is string type 
            /// <para> string type contaion:char、varchar、varchar2、nchar、nvarchar、nvarchar2,date、time、timestamp </para>
            /// </summary>
            public System.Data.DbType DataColumnType
            {
                get; set;
            }
            /// <summary>
            /// 属性枚举
            /// </summary>
            public IList<CustomPropertyAttributeEnum> Attributes { get; set; }


            /// <summary>
            /// 是否支持插入
            /// </summary>
            public bool IsSupportInsert { get; set; } = true;


            /// <summary>
            /// 是否支持更新
            /// </summary>
            public bool IsSupportUpdate { get; set; } = true;


            /// <summary>
            /// 是否支持查询
            /// </summary>
            public bool IsSupportSelect { get; set; } = true;


            public CustomColumnMapping Initialize(PropertyInfo property)
            {
                var name = _column_name_resolver.ResolveColumnName(property);
                this.ColumnName = property.Name;
                this.DataColumnName = name;
                this.ColumnType = property.PropertyType;

                SetDataColumnType(ColumnType);
                var isKey = IsKeyPropertie(property);
                this.IsKey = isKey;
                var isCode = IsCodePropertie(property);
                this.IsCode = isCode;
                InitializeAttributes(property);
                return this;
            }


            /// <summary>
            /// init 
            /// </summary>
            private void InitializeAttributes(PropertyInfo property)
            {
                SetIsSupportInsert(property);
                SetIsSupportUpdate(property);
                SetIsSupportSelect(property);


            }

            /// <summary>
            /// is support insert
            /// </summary>
            private void SetIsSupportInsert(PropertyInfo property)
            {
                if (IsKey)
                {
                    IsSupportInsert = false;
                    return;
                }
                var ignoreInsertAttribute = property.GetCustomAttribute(typeof(IgnoreInsertAttribute)) as IgnoreInsertAttribute;
                if (ignoreInsertAttribute != null)
                {
                    IsSupportInsert = false;
                    return;
                }
                var notMappedAttribute = property.GetCustomAttribute(typeof(NotMappedAttribute)) as NotMappedAttribute;
                if (notMappedAttribute != null)
                {
                    IsSupportInsert = false;
                    return;
                }

            }


            /// <summary>
            /// set is support update 
            /// </summary>
            private void SetIsSupportUpdate(PropertyInfo property)
            {
                if (IsKey || IsCode)
                {
                    IsSupportUpdate = false;
                    return;
                }
                var ignoreUpdateAttribute = property.GetCustomAttribute(typeof(IgnoreUpdateAttribute)) as IgnoreUpdateAttribute;
                if (ignoreUpdateAttribute != null)
                {
                    IsSupportUpdate = false;
                    return;
                }
                var notMappedAttribute = property.GetCustomAttribute(typeof(NotMappedAttribute)) as NotMappedAttribute;
                if (notMappedAttribute != null)
                {
                    IsSupportUpdate = false;
                    return;
                }

            }

            /// <summary>
            /// is support select
            /// </summary>
            private void SetIsSupportSelect(PropertyInfo property)
            {
                var ignoreUpdateAttribute = property.GetCustomAttribute(typeof(IgnoreSelectAttribute)) as IgnoreSelectAttribute;
                if (ignoreUpdateAttribute != null)
                {
                    IsSupportSelect = false;
                    return;
                }
                var notMappedAttribute = property.GetCustomAttribute(typeof(NotMappedAttribute)) as NotMappedAttribute;
                if (notMappedAttribute != null)
                {
                    IsSupportSelect = false;
                    return;
                }

            }




            /// <summary>
            ///  is key 
            /// </summary>
            /// <param name="type"></param>
            /// <returns></returns>
            private bool IsKeyPropertie(PropertyInfo propertyInfo)
            {
                var result = false;
                //优先获取Key标签的为主建，如果没有则再使用Id名的为主建
                var keyAttribute = propertyInfo.GetCustomAttribute(typeof(KeyAttribute)) as KeyAttribute;

                if (keyAttribute != null)
                {
                    return true;

                }

                if (propertyInfo.Name.Equals(DapperConfiguration.defalut_entity_key, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }

                return false;
            }

            /// <summary>
            ///  is code
            /// </summary>
            /// <param name="type"></param>
            /// <returns></returns>
            private bool IsCodePropertie(PropertyInfo propertyInfo)
            {
                var result = false;
                //优先获取Key标签的为主建，如果没有则再使用Id名的为主建
                var keyAttribute = propertyInfo.GetCustomAttribute(typeof(CodeAttribute)) as CodeAttribute;
                if (keyAttribute != null)
                {
                    return true;

                }
                return false;
            }

            private void SetDataColumnType(Type type)
            {
                DbType? dbType;

                SqlMapper.ITypeHandler? handler = null;

#pragma warning disable 618
                dbType = SqlMapper.LookupDbType(type, this.DataColumnName, true, out handler);
#pragma warning disable 618
                this.DataColumnType = dbType != null ? dbType.Value : DbType.String;
            }


            public System.Data.DbType GetDataColumnType()
            {
                return this.DataColumnType;
            }


        }

        /// <summary>
        /// 自定义属性枚举
        /// </summary>
        public enum CustomPropertyAttributeEnum
        {
            /// <summary>
            /// 排查插入
            /// </summary>
            IgnoreInsertAttribute,
            /// <summary>
            /// 排除更新
            /// </summary>
            IgnoreUpdateAttribute,
            /// <summary>
            /// 排除查询
            /// </summary>
            IgnoreSelectAttribute,
            /// <summary>
            /// 
            /// </summary>
            NotMappedAttribute
        }
    }
}
